package com.iMusic.businessLogicLayer;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.security.Security;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;

import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import com.iMusic.businessLogicLayer.orderStatus.OrderStatus;
import com.iMusic.integrationLayer.DBConnection;
import com.iMusic.integrationLayer.DBConnectionManager;
import com.iMusic.util.Parameters;


import org.apache.commons.mail.*;


public class Order {

	long orderId = 0;
	boolean paid = false;
	OrderStatus status = null;
	Payment payment = null;
	Float total = (float) 0.0;
	String receiptNumber = null;
	Account account = null;
	Date dateCreated = null; 
	Date dateModified = null; 
	int federalTaxType = 0;
	int provincialTaxType = 0;
	int shippingType = 0;
	String trackingNumber = "";
	
	List<OrderContent> orderContent = null;
	
	static private DBConnectionManager dbConnMgr = null;
	

	
	
	/*
	 * Constructor
	 */
	public Order (Account acc) {
		account = acc;
	}

	/*
	 * Constructor
	 */
	public Order () {
	}

	/*
	 * Adds content
	 */
	public void addContent(long oCId, Integer pId, Integer qtt, Float pri) {
		if (orderContent == null) {
			orderContent = new ArrayList<OrderContent>();
		}
		orderContent.add(new OrderContent(pId, qtt, pri));
	}
	
	
	/*
	 * Generates receipt number
	 */
	public void generateReceipt() {

		receiptNumber = "";
		Random randomGenerator = new Random();
	    for (int idx = 1; idx <= 16; ++idx){
	      receiptNumber += randomGenerator.nextInt(10);
	      
	    }
	}

	
	/*
	 * Retrieves downloadable MP3s
	 */
	public List<Product> getDownloadables() throws SQLException {

		List<Product> prodList = new ArrayList<Product>();

		for (OrderContent oc : orderContent) {
			Product prod = Product.getProduct(account, oc.getProductId());
			if ( prod.getTypeName().startsWith("MP3")) {
				prodList.add(prod);
			}
		}
	    
	    return prodList;
	}

	/*
	 * Update order status
	 */
	static public void updateStatus (int orderId, int status) throws SQLException {
		dbConnMgr = DBConnectionManager.getDBConnectionManager();
		DBConnection dbConn = dbConnMgr.getDBConnection();
		
		dbConn.connect();
		
		String sqlStmt = "UPDATE ORDERPRODUCT SET Status = " + status + " " +
						"    , DateModified = curdate() " +
						"WHERE OrderId = " + orderId;
		System.out.println(sqlStmt);
		dbConn.execSQL(sqlStmt);
		dbConn.execSQL("commit");
		
		dbConn.disconnect();
		dbConnMgr.freeDBConnection(dbConn);
		
		if (status == OrderStatus.Cancelled.getCode()) {
			List<OrderContent> ocList = OrderContent.searchOrderContents(orderId);
			for (OrderContent oc : ocList) {
				Product prod = Product.getProduct(null, oc.getProductId());
				if (prod.getUnitsInStock() >= 0) {
					prod.restoreFromStock(oc.getQuantity());
				}
			}
		}
		
	}
	
	
	/*
	 * Update order Tracking Number
	 */
	static public void updateTrackingNumber (int orderId, String tn) throws SQLException {
		dbConnMgr = DBConnectionManager.getDBConnectionManager();
		DBConnection dbConn = dbConnMgr.getDBConnection();
		
		dbConn.connect();
		
		String sqlStmt = "UPDATE ORDERPRODUCT SET TrackingNumber = '" + tn + "' " +
						"    , DateModified = curdate() " +
						"WHERE OrderId = " + orderId;
		System.out.println(sqlStmt);
		dbConn.execSQL(sqlStmt);
		dbConn.execSQL("commit");
		
		dbConn.disconnect();
		dbConnMgr.freeDBConnection(dbConn);
	}
	
	/*
	 * Search orders by OrderStatus
	 */
	static public List<Order> searchOrders(OrderStatus status) throws SQLException {
		
		dbConnMgr = DBConnectionManager.getDBConnectionManager();
		DBConnection dbConn = dbConnMgr.getDBConnection();
		
		List<Order> listOrder = new ArrayList<Order>();
		
		dbConn.connect();
		
		String sqlStmt = "SELECT OrderId, AccountId, CardFlag, Payed, Status, DateCreated, DateModified, SumPrice, TrackingNumber FROM OrderProduct " +
				"WHERE Status = " + status.getCode() + " " +
				"ORDER BY OrderId";

		ResultSet rs = dbConn.query(sqlStmt);

		while (rs != null && rs.next()) {
		
			Order order = new Order();
			order.setOrderId(rs.getLong("OrderID"));
			order.setAccount(Account.getAccount(rs.getLong("AccountID")));
			order.setPaid(rs.getBoolean("Payed"));
			order.setStatus(OrderStatus.getObject(rs.getInt("Status")));
			order.setTotal(rs.getFloat("SumPrice"));
			order.setDateCreated(rs.getDate("DateCreated"));
			order.setDateModified(rs.getDate("DateModified"));
			order.setTrackingNumber(rs.getString("TrackingNumber"));
			
			listOrder.add(order);
		}

		rs.close();
		rs = null;
		
		dbConn.disconnect();
		dbConnMgr.freeDBConnection(dbConn);
		
		return listOrder;
	}
	
	/*
	 * Retrieves the orders of an account
	 */
	static public List<Order> searchOrders(Account account) throws SQLException {
		
		dbConnMgr = DBConnectionManager.getDBConnectionManager();
		DBConnection dbConn = null;
		dbConn = account.getDBConnection();
		
		List<Order> listOrder = new ArrayList<Order>();
		
		String sqlStmt = "SELECT OrderId, CardFlag, Payed, Status, DateCreated, DateModified, SumPrice, TrackingNumber FROM OrderProduct " +
				"WHERE AccountId = " + account.getAccountId() + " " +
				"ORDER BY OrderId;";


		ResultSet rs = dbConn.query(sqlStmt);

		while (rs != null && rs.next()) {
		
			Order order = new Order(account);
			order.setOrderId(rs.getLong("OrderID"));
			order.setPaid(rs.getBoolean("Payed"));
			order.setStatus(OrderStatus.getObject(rs.getInt("Status")));
			order.setTotal(rs.getFloat("SumPrice"));
			order.setDateCreated(rs.getDate("DateCreated"));
			order.setDateModified(rs.getDate("DateModified"));
			order.setTrackingNumber(rs.getString("TrackingNumber"));
			
			
			listOrder.add(order);
		}

		rs.close();
		rs = null;
		
		return listOrder;
	}
	/*
	 * Sends email to user
	 */
	public void sendEmail() {
		
		try {
			Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
			 
			Properties props = new Properties();
			props.setProperty("mail.transport.protocol", "smtp");
			props.setProperty("mail.host", Parameters.EMAIL_SERVER);
			props.put("mail.smtp.auth", "true");
			props.put("mail.smtp.port", "465");
			props.put("mail.smtp.socketFactory.port", "465");
			props.put("mail.smtp.socketFactory.class",
			"javax.net.ssl.SSLSocketFactory");
			props.put("mail.smtp.socketFactory.fallback", "false");
			props.setProperty("mail.smtp.quitwait", "false");
	 
			Session session = Session.getDefaultInstance(props,
					new javax.mail.Authenticator() 
			{
				protected PasswordAuthentication getPasswordAuthentication()
				{ return new PasswordAuthentication(Parameters.EMAIL_USERNAME,Parameters.EMAIL_PASSWD);	}
			});		
	 
			MimeMessage message = new MimeMessage(session);
			message.setSender(new InternetAddress(Parameters.EMAIL_USERNAME));
			message.setSubject("test");
			message.setContent("body", "text/plain");
			message.setRecipient(Message.RecipientType.TO, new InternetAddress(account.getEmail()));
	 
			
			Transport.send(message);
			
			/*
			Email email = new SimpleEmail();
			email.setDebug(true);
			email.setHostName(Parameters.EMAIL_SERVER);
			email.setSmtpPort(Parameters.EMAIL_PORT);
			email.setAuthenticator(new DefaultAuthenticator(Parameters.EMAIL_USERNAME, Parameters.EMAIL_PASSWD));
			email.setSSLOnConnect(true);
			//email.setTLS(true);
			//email.setSSL(true);
			email.setFrom(Parameters.EMAIL_USERNAME);
			email.setSubject("Order Number " + this.getOrderId());
			email.setMsg("This is a test mail ... :-)");
			email.addTo(account.getEmail());
			email.send();
			*/
		}
		catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}

	/*
	 * Stores the order in the database
	 */
	public boolean storeOrder() throws SQLException {
		boolean ret = false;
		DBConnection dbConn = null;

		dbConnMgr = DBConnectionManager.getDBConnectionManager();
		dbConn = dbConnMgr.getDBConnection();
		dbConn.connect();
		
		Integer nextOrderId = 1;
		ResultSet rs = dbConn.query("SELECT MAX(OrderId)+1 FROM OrderProduct");
		if (rs.next()) {
			nextOrderId = rs.getInt(1);
			if (nextOrderId <= 0) {
				nextOrderId = 1;
			}
		}
		
		
		String sqlStmt = "INSERT INTO OrderProduct (OrderId, AccountId, Payed, Status, DateCreated, DateModified, SumPrice, CardFlag, " +
							"FederalTaxType, ProvincialTaxType, ShippingType) " +
							"VALUES (" + nextOrderId + ", " + account.getAccountId() +", true, " + status.getCode() + ", curdate(), curdate(), " +
							total  + ", '" + payment.getCardFlag() + "', " + federalTaxType + ", " + provincialTaxType + ", " +
							shippingType + ")";
		System.out.println(sqlStmt);
		dbConn.execSQL(sqlStmt);
		dbConn.execSQL("commit");
		
		orderId = nextOrderId;
		
		int orderContentId = 1;
		for (OrderContent oc : orderContent) {
			sqlStmt = "INSERT INTO OrderContent (OrderContentId, OrderId, ProductId, Quantity, Price) " +
						"VALUES (" + orderContentId++ + ", " + nextOrderId + ", " + oc.getProductId() + ", " +
						oc.getQuantity() + ", " + oc.getPrice() + ")";
			System.out.println(sqlStmt);
			dbConn.execSQL(sqlStmt);
			dbConn.execSQL("commit");
			
			// Removes products from stock
			Product prod = Product.getProduct(account, oc.getProductId());
			if (prod.getUnitsInStock() >= 0) {
				prod.removeFromStock(oc.getQuantity());
			}
		}
						
		dbConn.disconnect();
		dbConnMgr.freeDBConnection(dbConn);
		
		return ret;
	}
	
	
	
	public long getOrderId() {
		return orderId;
	}

	public void setOrderId(long orderId) {
		this.orderId = orderId;
	}

	public boolean isPaid() {
		return paid;
	}

	public void setPaid(boolean paid) {
		this.paid = paid;
		
		if (paid) {
			
		}
		
	}

	
	public OrderStatus getStatus() {
		return status;
	}

	public void setStatus(OrderStatus status) {
		this.status = status;
	}

	public List<OrderContent> getOrderContent() {
		return orderContent;
	}

	public void setOrderContent(List<OrderContent> orderContent) {
		this.orderContent = orderContent;
	}

	public Payment getPayment() {
		return payment;
	}

	public void setPayment(Payment payment) {
		this.payment = payment;
	}

	public Float getTotal() {
		return total;
	}

	public void setTotal(Float total) {
		this.total = total;
	}

	public String getReceiptNumber() {
		return receiptNumber;
	}

	public void setReceiptNumber(String receiptNumber) {
		this.receiptNumber = receiptNumber;
	}

	public Date getDateCreated() {
		return dateCreated;
	}

	public void setDateCreated(Date dateCreated) {
		this.dateCreated = dateCreated;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public int getShippingType() {
		return shippingType;
	}

	public void setShippingType(int shippingType) {
		this.shippingType = shippingType;
	}

	public int getFederalTaxType() {
		return federalTaxType;
	}

	public void setFederalTaxType(int federalTaxType) {
		this.federalTaxType = federalTaxType;
	}

	public int getProvincialTaxType() {
		return provincialTaxType;
	}

	public void setProvincialTaxType(int provincialTaxType) {
		this.provincialTaxType = provincialTaxType;
	}

	public Date getDateModified() {
		return dateModified;
	}

	public void setDateModified(Date dateModified) {
		this.dateModified = dateModified;
	}

	public String getTrackingNumber() {
		return trackingNumber;
	}

	public void setTrackingNumber(String trackingNumber) {
		this.trackingNumber = trackingNumber;
	}
	
	
}
